package com.leyou.search.service.impl;

import com.leyou.common.dto.PageDTO;
import com.leyou.common.exception.LyException;
import com.leyou.item.client.ItemClient;
import com.leyou.item.dto.ReqSpuDTO;
import com.leyou.item.dto.SkuDTO;
import com.leyou.item.dto.SpecParamDTO;
import com.leyou.item.dto.SpuDTO;
import com.leyou.search.dto.SearchParamDTO;
import com.leyou.search.entity.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.SearchService;
import com.leyou.starter.elastic.dto.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.*;
import java.util.stream.Collectors;

import static com.leyou.search.constants.SearchConstants.*;

/**
 * @Author: SGS
 * @Date : 2020/11/22 20:21
 * @Version :
 */
@Slf4j
@Service
public class SearchServiceImpl implements SearchService {

    private final GoodsRepository goodsRepository;
    private final ItemClient itemClient;

    public SearchServiceImpl(GoodsRepository goodsRepository, ItemClient itemClient) {
        this.goodsRepository = goodsRepository;
        this.itemClient = itemClient;
    }


    @Override
    public void createIndexAndMapping() {
        //删除已经存在的索引库
        try {
            goodsRepository.deleteIndex();
        } catch (Exception e) {
            log.error("删除失败,可能索引库不存在!", e);
        }

        //创建新的索引库
        goodsRepository.createIndex("{\n" +
                "  \"settings\": {\n" +
                "    \"analysis\": {\n" +
                "      \"analyzer\": {\n" +
                "        \"my_pinyin\": {\n" +
                "          \"tokenizer\": \"ik_smart\",\n" +
                "          \"filter\": [\n" +
                "            \"py\"\n" +
                "          ]\n" +
                "        }\n" +
                "      },\n" +
                "      \"filter\": {\n" +
                "        \"py\": {\n" +
                "\t\t  \"type\": \"pinyin\",\n" +
                "          \"keep_full_pinyin\": true,\n" +
                "          \"keep_joined_full_pinyin\": true,\n" +
                "          \"keep_original\": true,\n" +
                "          \"limit_first_letter_length\": 16,\n" +
                "          \"remove_duplicated_term\": true\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  },\n" +
                "  \"mappings\": {\n" +
                "    \"properties\": {\n" +
                "      \"id\": {\n" +
                "        \"type\": \"keyword\"\n" +
                "      },\n" +
                "      \"suggestion\": {\n" +
                "        \"type\": \"completion\",\n" +
                "        \"analyzer\": \"my_pinyin\",\n" +
                "        \"search_analyzer\": \"ik_smart\"\n" +
                "      },\n" +
                "      \"title\":{\n" +
                "        \"type\": \"text\",\n" +
                "        \"analyzer\": \"my_pinyin\",\n" +
                "        \"search_analyzer\": \"ik_smart\"\n" +
                "      },\n" +
                "      \"image\":{\n" +
                "        \"type\": \"keyword\",\n" +
                "        \"index\": false\n" +
                "      },\n" +
                "      \"updateTime\":{\n" +
                "        \"type\": \"date\"\n" +
                "      },\n" +
                "      \"specs\":{\n" +
                "        \"type\": \"nested\",\n" +
                "        \"properties\": {\n" +
                "          \"name\":{\"type\": \"keyword\" },\n" +
                "          \"value\":{\"type\": \"keyword\" }\n" +
                "        }\n" +
                "      }\n" +
                "    }\n" +
                "  }\n" +
                "}");
    }

    @Override
    public void loadData() {
        int page = 1, rows = 50;
        while (true) {
            log.info("开始导入第{}页数据", page);
            //分页查询已经上架的spu
            PageDTO<SpuDTO> result = itemClient.querySpuByPage(new ReqSpuDTO(null, null, null, page, rows, true));
            List<SpuDTO> list = result.getItems();
            //遍历spu集合,把SpuDTO通过buildGoods方法转为Goods
            List<Goods> goodsList = list.stream().map(this::buildGoods).collect(Collectors.toList());

            //批量写入Elasticsearch
            goodsRepository.saveAll(goodsList);
            log.info("导入第{}页数据结束", page);
            //翻页
            page++;
            //获取总页数
            Long totalPage = result.getTotalPage();
            if (page > totalPage) {
                break;
            }
        }
    }

    /**
     * 将查询到的Spu变为Goods对象
     *
     * @param spuDTO
     * @return
     */
    private Goods buildGoods(SpuDTO spuDTO) {
        //自动补全的提示字段
        ArrayList<String> suggestion = new ArrayList<>();
        Arrays.asList(StringUtils.split(spuDTO.getCategoryName(), "/"));
        suggestion.add(spuDTO.getName());
        suggestion.add(spuDTO.getBrandName());
        //sku的价格集合
        //查询sku集合
        List<SkuDTO> skus = spuDTO.getSkus();
        if (CollectionUtils.isEmpty(skus)) {
            //没有sku,去查询
            skus = itemClient.querySkuBySpuId(spuDTO.getId());
        }
        //获取价格集合,set集合去重
        Set<Long> prices = skus.stream().map(SkuDTO::getPrice).collect(Collectors.toSet());
        //商品销量
        long sold = skus.stream().mapToLong(SkuDTO::getSold).sum();
        //获取sku的第一张图片
        String image = StringUtils.substringBefore(skus.get(0).getImages(), ",");

        //规格参数
        List<Map<String, Object>> specs = new ArrayList<Map<String, Object>>();
        //查询规格参数name和value键值对,只查询参与搜索的
        List<SpecParamDTO> params = itemClient.queryParamBySpuId(spuDTO.getId(), true);
        //封装
        for (SpecParamDTO param : params) {
            Map<String, Object> map = new HashMap<>(2);
            map.put("name", param.getName());
            map.put("value", chooseSegment(param));
            specs.add(map);
        }


        Goods goods = new Goods();
        //商品价格
        goods.setPrices(prices);
        goods.setSold(sold);
        goods.setImage(image);
        goods.setSpecs(specs);
        goods.setUpdateTime(new Date());
        goods.setTitle(spuDTO.getTitle() + StringUtils.join(suggestion, " "));
        goods.setSuggestion(suggestion);
        goods.setId(spuDTO.getId());
        goods.setCategoryId(spuDTO.getCid3());
        goods.setBrandId(spuDTO.getBrandId());

        return goods;
    }

    /**
     * 处理数字value的代码
     *
     * @param param
     * @return
     */
    private Object chooseSegment(SpecParamDTO param) {
        Object value = param.getValue();
        if (value == null || StringUtils.isBlank(value.toString())) {
            return "其它";
        }
        if (!param.getNumeric() || StringUtils.isBlank(param.getSegments()) || value instanceof Collection) {
            return value;
        }
        double val = parseDouble(value.toString());
        String result = "其它";
        //保存数值段
        for (String segment : param.getSegments().split(",")) {
            String[] segs = segment.split("-");
            //获取数值范围
            double begin = parseDouble(segs[0]);
            double end = Double.MAX_VALUE;
            if (segs.length == 2) {
                end = parseDouble(segs[1]);
            }
            //判断是否在范围内
            if (val >= begin && val < end) {
                if (segs.length == 1) {
                    result = segs[0] + param.getUnit() + "以上";
                } else if (begin == 0) {
                    result = segs[1] + param.getUnit() + "以下";
                } else {
                    result = segment + param.getUnit();
                }
                break;
            }
        }
        return result;
    }

    private double parseDouble(String str) {
        try {
            return Double.parseDouble(str);
        } catch (NumberFormatException e) {
            return 0;
        }

    }


    /**
     * 查询补全搜索框
     *
     * @param key 请求补全字段
     * @return
     */
    @Override
    public Mono<List<String>> getSuggestion(String key) {
        if (StringUtils.isBlank(key)) {
            throw new LyException(400, "请求参数为空");
        }
        return goodsRepository.suggestBySingleField(SUGGESTION_FIELD, key);
    }

    /**
     * 分页查询搜索商品数据
     *
     * @param request
     * @return
     */
    @Override
    public Mono<PageInfo<Goods>> searchGoods(SearchParamDTO request) {
        //构建查询条件的工厂对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //1.1source过滤
        sourceBuilder.fetchSource(DEFAULT_SOURCE_FIELD, new String[0]);
        //查询条件
        String key = request.getKey();
        if (StringUtils.isBlank(key)) {
            //搜索条件为null,返回异常
            throw new LyException(400, "搜索条件不能为空!");
        }
        sourceBuilder.query(QueryBuilders.matchQuery(DEFAULT_SEARCH_FIELD, key));
        //分页条件
        sourceBuilder.from(request.getFrom());
        sourceBuilder.size(request.getSize());
        //排序条件
        if (StringUtils.isNotBlank(request.getSortBy())) {
            //排序字段存在,可以排序
            sourceBuilder.sort(request.getSortBy(), request.getDesc() ? SortOrder.DESC : SortOrder.ASC);
        }
        //高亮条件
        sourceBuilder.highlighter(new HighlightBuilder().field(DEFAULT_SEARCH_FIELD)
                .preTags(DEFAULT_PRE_TAG).postTags(DEFAULT_POST_TAG));
        //2.查询
        return goodsRepository.queryBySourceBuilderForPageHighlight(sourceBuilder);


    }

    @Override
    public void saveGoodsById(Long spuId) {
        //需要spu
        SpuDTO spu = itemClient.querySpuSkuDetailById(spuId);
        System.out.println("*********新增了");
        //构建goods
        Goods goods = buildGoods(spu);
        //写入所引库
        goodsRepository.save(goods);
    }

    @Override
    public void deleteGoodsById(Long spuId) {
        System.out.println("************删除了");
            goodsRepository.deleteById(spuId);
    }

}
